home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue33 / undo / TextWin.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-02-20  |  11.7 KB  |  454 lines

  1. unit TextWin;
  2.  
  3. { This unit contains code for an undoable memo-based text window.
  4.  
  5.   Author : Warren Kovach (wlk@kovcomp.co.uk)
  6.   Published in The Delphi Magazine }
  7.  
  8. interface
  9.  
  10. uses WinTypes, WinProcs, Messages,Classes, Graphics, Forms, Controls, StdCtrls,
  11.      SysUtils, Dialogs, Undo, Menus;
  12.  
  13. {$IFDEF WIN32}
  14.   {$IFDEF VER90}
  15. const
  16.   {$ELSE}
  17. resourcestring
  18.   {$ENDIF}
  19. {$ELSE}
  20. const
  21. {$ENDIF}
  22.   sTextUndoDescr = 'Undo typing "%s"';
  23.   sTextShortUndoDescr = 'Undo "%s"';
  24.   sTextRedoDescr = 'Redo typing "%s"';
  25.   sTextShortRedoDescr = 'Redo "%s"';
  26.   sTextUndoMenu = '&Undo typing';
  27.   sTextRedoMenu = '&Redo typing';
  28.   sClearUndoDescr = 'Undo deleting "%s"';
  29.   sClearShortUndoDescr = 'Undo delete';
  30.   sClearRedoDescr = 'Redo deleting "%s"';
  31.   sClearShortRedoDescr = 'Redo delete';
  32.   sClearUndoMenu = '&Undo deleting';
  33.   sClearRedoMenu = '&Redo deleting';
  34.   sFontUndoDescr = 'Undo change font';
  35.   sFontShortUndoDescr = 'Undo font';
  36.   sFontRedoDescr = 'Redo change font';
  37.   sFontShortRedoDescr = 'Redo font';
  38.   sFontUndoMenu = '&Undo font';
  39.   sFontRedoMenu = '&Redo font';
  40.  
  41. type
  42.   string2 = string[2];
  43.  
  44.   TClearingUndoItem = class(TUndoItem)
  45.     private
  46.       StartPos   : integer;
  47.       DeletedText  : string;
  48.       Editor : TMemo;
  49.       function GetDescr(Msg,AText : string;TextLength : integer):string;
  50.     protected
  51.       function GetUndoDescription : string; override;
  52.       function GetShortUndoDescription : string; override;
  53.       function GetRedoDescription : string; override;
  54.       function GetShortRedoDescription : string; override;
  55.       function GetUndoMenuText : string; override;
  56.       function GetRedoMenuText : string; override;
  57.     public
  58.       constructor Create(AEditor : TMemo;ADeletedText : string;
  59.                          APosition : integer);
  60.       procedure DoCommand; override;
  61.       procedure Undo; override;
  62.       procedure Redo; override;
  63.   end;
  64.  
  65.   TTypingUndoItem = class(TClearingUndoItem)
  66.     private
  67.       CurPos   : integer;
  68.       InsertedText  : string;
  69.     protected
  70.       function GetUndoDescription : string; override;
  71.       function GetShortUndoDescription : string; override;
  72.       function GetRedoDescription : string; override;
  73.       function GetShortRedoDescription : string; override;
  74.       function GetUndoMenuText : string; override;
  75.       function GetRedoMenuText : string; override;
  76.     public
  77.       constructor Create(AEditor : TMemo;AInsertedText,ADeletedText : string;
  78.                          APosition : integer);
  79.       procedure AddText(AText : string2; APos : integer);
  80.       procedure Undo; override;
  81.       procedure Redo; override;
  82.   end;
  83.  
  84.   TFontChangeUndoItem = class(TUndoItem)
  85.     private
  86.       Font,
  87.       OldFont : TFont;
  88.       Editor : TMemo;
  89.     protected
  90.       function GetUndoDescription : string; override;
  91.       function GetShortUndoDescription : string; override;
  92.       function GetRedoDescription : string; override;
  93.       function GetShortRedoDescription : string; override;
  94.       function GetUndoMenuText : string; override;
  95.       function GetRedoMenuText : string; override;
  96.     public
  97.       constructor Create(AEditor : TMemo;AFont : TFont);
  98.       destructor destroy; override;
  99.       procedure DoCommand; override;
  100.       procedure Undo; override;
  101.       procedure Redo; override;
  102.   end;
  103.  
  104.   TTextWindow = class(TUndoForm)
  105.     Memo1: TMemo;
  106.     FontDialog1: TFontDialog;
  107.     PopupMenu1: TPopupMenu;
  108.     Cut1: TMenuItem;
  109.     Copy1: TMenuItem;
  110.     Paste1: TMenuItem;
  111.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  112.     procedure Memo1KeyDown(Sender: TObject; var Key: Word;
  113.       Shift: TShiftState);
  114.     procedure FormCreate(Sender: TObject);
  115.     procedure Memo1MouseDown(Sender: TObject; Button: TMouseButton;
  116.       Shift: TShiftState; X, Y: Integer);
  117.   private
  118.     { Private declarations }
  119.     Typing            : boolean;
  120.     procedure EndTyping;
  121.   public
  122.     { Public declarations }
  123.     procedure ChangeFont;
  124.   end;
  125.  
  126. var
  127.   TextWindow : TTextWindow;
  128.  
  129. implementation
  130.  
  131. {$R *.DFM}
  132.  
  133. constructor TClearingUndoItem.Create(AEditor : TMemo;ADeletedText : string;
  134.                    APosition : integer);
  135. begin
  136.   inherited Create;
  137.   StartPos := APosition;
  138.   DeletedText := ADeletedText;
  139.   Editor := AEditor;
  140. end;
  141.  
  142. procedure TClearingUndoItem.DoCommand;
  143. begin
  144.   ;
  145. end;
  146.  
  147. procedure TClearingUndoItem.Undo;
  148. var
  149.   TempText : string;
  150. begin
  151.   TempText := Editor.Text;
  152.   Insert(DeletedText,TempText,succ(StartPos));
  153.   Editor.Text := TempText;
  154.   Editor.SelStart := StartPos;
  155.   {$IFDEF Win32}
  156.   Editor.Perform(EM_SCROLLCARET,0,0);
  157.   {$ENDIF}
  158. end;
  159.  
  160. procedure TClearingUndoItem.Redo;
  161. var
  162.   TempText : string;
  163. begin
  164.   TempText := Editor.Text;
  165.   Delete(TempText,succ(StartPos),length(DeletedText));
  166.   Editor.Text := TempText;
  167.   Editor.SelStart := StartPos;
  168.   {$IFDEF Win32}
  169.   Editor.Perform(EM_SCROLLCARET,0,0);
  170.   {$ENDIF}
  171. end;
  172.  
  173. function TClearingUndoItem.GetDescr(Msg,AText : string;TextLength : integer):string;
  174. var
  175.   TypedText : string;
  176. begin
  177.   TypedText := Copy(AText,1,TextLength);
  178.   if length(AText) > length(TypedText) then
  179.     TypedText := TypedText + '...';
  180.   Result := Format(Msg,[TypedText]);
  181. end;
  182.  
  183. function TClearingUndoItem.GetUndoDescription : string;
  184. begin
  185.   Result := GetDescr(sClearUndoDescr,DeletedText,25);
  186. end;
  187.  
  188. function TClearingUndoItem.GetShortUndoDescription : string;
  189. begin
  190.   Result := GetDescr(sClearShortRedoDescr,DeletedText,10);
  191. end;
  192.  
  193. function TClearingUndoItem.GetRedoDescription : string;
  194. begin
  195.   Result := GetDescr(sClearRedoDescr,DeletedText,25);
  196. end;
  197.  
  198. function TClearingUndoItem.GetShortRedoDescription : string;
  199. begin
  200.   Result := GetDescr(sClearShortRedoDescr,DeletedText,10);
  201. end;
  202.  
  203. function TClearingUndoItem.GetUndoMenuText : string;
  204. begin
  205.   Result := sClearUndoMenu;
  206. end;
  207.  
  208. function TClearingUndoItem.GetRedoMenuText : string;
  209. begin
  210.   Result := sClearRedoMenu;
  211. end;
  212. { ------------------------------------------------ }
  213. constructor TTypingUndoItem.Create(AEditor : TMemo;AInsertedText,ADeletedText : string;
  214.                    APosition : integer);
  215. begin
  216.   inherited Create(AEditor,ADeletedText,APosition);
  217.   AddText(AInsertedText,APosition);
  218. end;
  219.  
  220. procedure TTypingUndoItem.AddText(AText : string2;APos : integer);
  221. const
  222.   BackSpace = #08;
  223.   CR        = #13;
  224.   LF        = #10;
  225. var
  226.   Temp : integer;
  227. begin
  228.   if AText = CR then begin
  229.     AText := AText + LF;
  230.     CurPos := APos + 2;
  231.   end
  232.   else if AText[1] = BackSpace then begin
  233.     if APos > 0 then begin
  234.       if InsertedText = '' then
  235.         Insert(Editor.Text[(APos)],DeletedText,1)
  236.       else
  237.         Delete(InsertedText,length(InsertedText),1);
  238.       CurPos := pred(APos);
  239.     end;
  240.   end
  241.   else begin
  242.     InsertedText := InsertedText + AText;
  243.     CurPos := succ(APos);
  244.   end;
  245. end;
  246.  
  247. procedure TTypingUndoItem.Undo;
  248. var
  249.   TempText : string;
  250. begin
  251.   TempText := Editor.Text;
  252.   Delete(TempText,succ(StartPos),length(InsertedText));
  253.   if DeletedText <> '' then
  254.     Insert(DeletedText,TempText,succ(StartPos));
  255.   Editor.Text := TempText;
  256.   Editor.SelStart := StartPos;
  257.   {$IFDEF Win32}
  258.   Editor.Perform(EM_SCROLLCARET,0,0);
  259.   {$ENDIF}
  260. end;
  261.  
  262. procedure TTypingUndoItem.Redo;
  263. var
  264.   TempText : string;
  265. begin
  266.   TempText := Editor.Text;
  267.   if DeletedText <> '' then
  268.     if StartPos > CurPos then
  269.       Delete(TempText,succ(CurPos),length(DeletedText))
  270.     else
  271.       Delete(TempText,succ(StartPos),length(DeletedText));
  272.   Insert(InsertedText,TempText,succ(StartPos));
  273.   Editor.Text := TempText;
  274.   Editor.SelStart := CurPos;
  275.   {$IFDEF Win32}
  276.   Editor.Perform(EM_SCROLLCARET,0,0);
  277.   {$ENDIF}
  278. end;
  279.  
  280. function TTypingUndoItem.GetUndoDescription : string;
  281. begin
  282.   Result := GetDescr(sTextUndoDescr,InsertedText,25);
  283. end;
  284.  
  285. function TTypingUndoItem.GetShortUndoDescription : string;
  286. begin
  287.   Result := GetDescr(sTextShortRedoDescr,InsertedText,10);
  288. end;
  289.  
  290. function TTypingUndoItem.GetRedoDescription : string;
  291. begin
  292.   Result := GetDescr(sTextRedoDescr,InsertedText,25);
  293. end;
  294.  
  295. function TTypingUndoItem.GetShortRedoDescription : string;
  296. begin
  297.   Result := GetDescr(sTextShortRedoDescr,InsertedText,10);
  298. end;
  299.  
  300. function TTypingUndoItem.GetUndoMenuText : string;
  301. begin
  302.   Result := sTextUndoMenu;
  303. end;
  304.  
  305. function TTypingUndoItem.GetRedoMenuText : string;
  306. begin
  307.   Result := sTextRedoMenu;
  308. end;
  309. { ------------------------------------------------ }
  310. constructor TFontChangeUndoItem.Create(AEditor : TMemo;AFont : TFont);
  311. begin
  312.   inherited Create;
  313.   Editor := AEditor;
  314.   Font := TFont.Create;
  315.   OldFont := TFont.Create;
  316.   Font.Assign(AFont);
  317. end;
  318.  
  319. destructor TFontChangeUndoItem.destroy;
  320. begin
  321.   Font.Free;
  322.   OldFont.Free;
  323.   inherited destroy;
  324. end;
  325.  
  326. procedure TFontChangeUndoItem.DoCommand;
  327. begin
  328.   OldFont.Assign(Editor.Font);
  329.   Editor.Font.Assign(Font);
  330. end;
  331.  
  332. procedure TFontChangeUndoItem.Undo;
  333. begin
  334.   Editor.Font.Assign(OldFont);
  335. end;
  336.  
  337. procedure TFontChangeUndoItem.Redo;
  338. begin
  339.   Editor.Font.Assign(Font);
  340. end;
  341.  
  342. function TFontChangeUndoItem.GetUndoDescription : string;
  343. begin
  344.   Result := sFontUndoDescr;
  345. end;
  346.  
  347. function TFontChangeUndoItem.GetShortUndoDescription : string;
  348. begin
  349.   Result := sFontShortRedoDescr;
  350. end;
  351.  
  352. function TFontChangeUndoItem.GetRedoDescription : string;
  353. begin
  354.   Result := sFontRedoDescr;
  355. end;
  356.  
  357. function TFontChangeUndoItem.GetShortRedoDescription : string;
  358. begin
  359.   Result := sFontShortRedoDescr;
  360. end;
  361.  
  362. function TFontChangeUndoItem.GetUndoMenuText : string;
  363. begin
  364.   Result := sFontUndoMenu;
  365. end;
  366.  
  367. function TFontChangeUndoItem.GetRedoMenuText : string;
  368. begin
  369.   Result := sFontRedoMenu;
  370. end;
  371. { ------------------------------------------------ }
  372. procedure TTextWindow.FormCreate(Sender: TObject);
  373. begin
  374.   Typing := false;
  375. end;
  376.  
  377. procedure TTextWindow.FormClose(Sender: TObject; var Action: TCloseAction);
  378. begin
  379.   Action := caFree;
  380. end;
  381.  
  382. procedure TTextWindow.EndTyping;
  383. begin
  384.   if Typing then begin
  385.     Typing := false;
  386.   end;
  387. end;
  388.  
  389. procedure TTextWindow.Memo1KeyDown(Sender: TObject; var Key: Word;
  390.   Shift: TShiftState);
  391. var
  392.   KeyState : TKeyboardState;
  393.   Buffer : array[0..2] of char;
  394.   ToASCIIResult : integer;
  395.   Item : TUndoItem;
  396. begin
  397.   Item := nil;
  398.   with Memo1 do
  399.     if (SelLength > 0) and (key in [VK_Delete,VK_Back]) then begin
  400.       EndTyping;
  401.       Item := TClearingUndoItem.Create(Memo1,SelText,SelStart);
  402.     end
  403.     else if key = VK_Delete then begin
  404.       Item := TClearingUndoItem.Create(Memo1,Text[succ(SelStart)],SelStart);
  405.     end
  406.     else begin
  407.       FillChar(Buffer,sizeof(Buffer),0);
  408.       GetKeyboardState(KeyState);
  409.       {$IFDEF Win32}
  410.       ToASCIIResult := ToASCII(Key,MapVirtualKey(Key,0),KeyState,Buffer,0);
  411.       {$ELSE}
  412.       ToASCIIResult := ToASCII(Key,MapVirtualKey(Key,0),@KeyState,@Buffer,0);
  413.       {$ENDIF}
  414.       if ToASCIIResult > 0 then begin
  415.         if not Typing then begin
  416.           Item := TTypingUndoItem.Create(Memo1,StrPas(Buffer),SelText,SelStart);
  417.           Typing := true;
  418.         end
  419.         else
  420.           with UndoStack.CurrentItem as TTypingUndoItem do
  421.             AddText(StrPas(Buffer),(SelStart));
  422.       end
  423.       else begin
  424.         EndTyping;
  425.       end;
  426.     end;
  427.   if Item <> nil then
  428.     if UndoStack.Submit(Item) = ssFull then
  429.       ShowMessage(Format(sStackFull,[UndoStack.MaxItems]));
  430. end;
  431.  
  432. procedure TTextWindow.Memo1MouseDown(Sender: TObject; Button: TMouseButton;
  433.   Shift: TShiftState; X, Y: Integer);
  434. begin
  435.   EndTyping;
  436. end;
  437.  
  438. procedure TTextWindow.ChangeFont;
  439. var
  440.   Item : TUndoItem;
  441. begin
  442.   EndTyping;
  443.   with FontDialog1 do begin
  444.     Font := Memo1.Font;
  445.     if Execute then begin
  446.       Item := TFontChangeUndoItem.Create(Memo1,Font);
  447.       if UndoStack.Submit(Item) = ssFull then
  448.         ShowMessage(Format(sStackFull,[UndoStack.MaxItems]));
  449.     end;
  450.   end;
  451. end;
  452.  
  453. end.
  454.